// Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};
use sha2::{digest::Output, Sha256};

use crate::utils::{sha256_deserialize, sha256_serialize};

#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct IndexConfig(pub BTreeMap<String, PkgIndexEntry>);

#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PkgIndexEntry {
    pub stat: IndexItem,
    pub files: PkgIndexFiles,
}

#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct IndexItem {
    pub path: String,
    #[serde(serialize_with = "sha256_serialize", deserialize_with = "sha256_deserialize")]
    pub sha256: Output<Sha256>,
}

#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PkgIndexFiles {
    Compressed(IndexItem),
    CompressedVolumes(Vec<IndexItem>),
}

impl IndexConfig {
    pub fn empty() -> Self {
        Self(BTreeMap::new())
    }

    pub fn contains(&self, spec_str: &str) -> bool {
        self.0.contains_key(spec_str)
    }
}

#[cfg(test)]
mod test {
    use crate::{config::{index::{IndexItem, PkgIndexEntry, PkgIndexFiles}, IndexConfig}, utils::sha256_from_str};

    #[test]
    fn test_index_config_serialize() {
        assert_eq!(
            toml::to_string_pretty(&IndexConfig([
                (
                    "pkg1".to_string(),
                    PkgIndexEntry {
                        stat: IndexItem {
                            path: "pkg1.stat".to_string(),
                            sha256: sha256_from_str("2222222222222222222222222222222222222222222222222222222222222222").unwrap(),
                        },
                        files: PkgIndexFiles::Compressed(IndexItem {
                            path: "pkg1.zip".to_string(),
                            sha256: sha256_from_str("cd328edeac92f6a665de9f323c93b712af1858bc2e0d88f3f7100469470a1b8a").unwrap(),
                        }),
                    }
                ),
            ].into_iter().collect())),
            Ok("[pkg1.stat]\n\
                path = \"pkg1.stat\"\n\
                sha256 = \"2222222222222222222222222222222222222222222222222222222222222222\"\n\
                \n\
                [pkg1.files]\n\
                path = \"pkg1.zip\"\n\
                sha256 = \"cd328edeac92f6a665de9f323c93b712af1858bc2e0d88f3f7100469470a1b8a\"\n".to_string())
        );
    }

    #[test]
    fn test_index_config_deserialize() {
        assert_eq!(
            toml::from_str::<IndexConfig>(
                "[pkg1]\n\
                 stat = { path = \"pkg1.stat\", sha256 = \"2222222222222222222222222222222222222222222222222222222222222222\" }\n\
                 files = { path = \"pkg1.zip\", sha256 = \"cd328edeac92f6a665de9f323c93b712af1858bc2e0d88f3f7100469470a1b8a\" }\n\
                 \n\
                 [pkg2]\n\
                 stat = { path = \"pkg2.stat\", sha256 = \"2222222222222222222222222222222222222222222222222222222222222222\" }\n\
                 files = [\n\
                     { path = \"pkg2.z01\", sha256 = \"2222222222222222222222222222222222222222222222222222222222222222\" },\n\
                     { path = \"pkg2.z02\", sha256 = \"2222222222222222222222222222222222222222222222222222222222222222\" },\n\
                 ]\n"
            ),
            Ok(IndexConfig([
                (
                    "pkg1".to_string(),
                    PkgIndexEntry {
                        stat: IndexItem {
                            path: "pkg1.stat".to_string(),
                            sha256: sha256_from_str("2222222222222222222222222222222222222222222222222222222222222222").unwrap(),
                        },
                        files: PkgIndexFiles::Compressed(IndexItem {
                            path: "pkg1.zip".to_string(),
                            sha256: sha256_from_str("cd328edeac92f6a665de9f323c93b712af1858bc2e0d88f3f7100469470a1b8a").unwrap(),
                        }),
                    }
                ),
                (
                    "pkg2".to_string(),
                    PkgIndexEntry {
                        stat: IndexItem {
                            path: "pkg2.stat".to_string(),
                            sha256: sha256_from_str("2222222222222222222222222222222222222222222222222222222222222222").unwrap(),
                        },
                        files: PkgIndexFiles::CompressedVolumes(vec![
                            IndexItem {
                                path: "pkg2.z01".to_string(),
                                sha256: sha256_from_str("2222222222222222222222222222222222222222222222222222222222222222").unwrap(),
                            },
                            IndexItem {
                                path: "pkg2.z02".to_string(),
                                sha256: sha256_from_str("2222222222222222222222222222222222222222222222222222222222222222").unwrap(),
                            },
                        ]),
                    }
                )
            ].into_iter().collect()))
        );
    }
}